package com.pwt.repository.base;


import com.pwt.entity.base.BaseEntity;
import com.pwt.security.utils.JWTUtils;
import com.pwt.utils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.util.Assert;

import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import java.beans.PropertyDescriptor;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

public class DataJpaRepository<T, ID> extends SimpleJpaRepository<T,ID> {
    private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!";

    private final JpaEntityInformation<T, ?> entityInformation;
    private final EntityManager em;

    @Autowired
    public DataJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityInformation = entityInformation;
        this.em = entityManager;
    }

    @Override
    public void deleteById(@NotNull ID id) {
        Assert.notNull(id, ID_MUST_NOT_BE_NULL);
        Optional<T> t = super.findById(id);
        if(t.isPresent()) {
            super.deleteById(id);
        }
    }

    /**
     * 通用save方法 ：新增/选择性更新
     */
    @Override
    @Transactional(rollbackOn = Exception.class)
    public <S extends T> S save(@NotNull S entity) {
        Assert.notNull(entity, "Entity must not be null.");
        //获取ID
        ID entityId = (ID) this.entityInformation.getId(entity);
        T managedEntity;
        T mergedEntity;
        String username = null;
        try {
            username = JWTUtils.getToken().getUsername();
        } catch (Exception e) {
        }

        if(entityId == null || (entityId instanceof String && "".equals(entityId.toString().trim()))){
            if(entity instanceof BaseEntity){
                if(StringUtils.isBlank(((BaseEntity) entity).getCreateBy())){
                    ((BaseEntity) entity).setCreateBy(username);
                }
            }
            this.em.persist(entity);
            mergedEntity = entity;
        }else{
            managedEntity = this.findById(entityId).orElse(null);
            if (managedEntity == null) {
                if(entity instanceof BaseEntity){
                    if(StringUtils.isBlank(((BaseEntity) entity).getUpdateBy())){
                        ((BaseEntity) entity).setUpdateBy(username);
                    }
                }
                this.em.persist(entity);
                mergedEntity = entity;
            } else {
                if(entity instanceof BaseEntity){
                    if(StringUtils.isBlank(((BaseEntity) entity).getCreateBy())){
                        ((BaseEntity) entity).setCreateBy(username);
                    }
                    ((BaseEntity) entity).setUpdateBy(username);
                }
                BeanUtils.copyProperties(entity, managedEntity, getNullProperties(entity));
                em.merge(managedEntity);
                mergedEntity = managedEntity;
            }
        }
        return (S) mergedEntity;
    }

    /**
     * 获取对象的空属性
     */
    private static String[] getNullProperties(Object src) {
        //1.获取Bean
        BeanWrapper srcBean = new BeanWrapperImpl(src);
        //2.获取Bean的属性描述
        PropertyDescriptor[] pds = srcBean.getPropertyDescriptors();
        //3.获取Bean的空属性
        Set<String> properties = new HashSet<>();
        for (PropertyDescriptor propertyDescriptor : pds) {
            String propertyName = propertyDescriptor.getName();
            Object propertyValue = srcBean.getPropertyValue(propertyName);
            if (propertyValue == null) {
                srcBean.setPropertyValue(propertyName, null);
                properties.add(propertyName);
            }
        }
        return properties.toArray(new String[0]);
    }
}
